// $Id: Main.cs 72 2008-06-24 20:30:03Z nvivo $

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using DBLGen.UI;

namespace DBLGen.GUI.Forms
{
	public sealed partial class Main : Form
	{
		private static readonly string FileDialogFilter = "DBLGen Project|*.dblgen|All files|*.*";

		private Project _currentProject;
		private bool _modified = false;

		public Main()
		{
			InitializeComponent();
			
			// Auto Load Project
			// CurrentProject = Project.Load("Base.Net.dblgen");
			CurrentProject = Project.Load("Quizbutler.dblgen");
			
			Modified = false;
			RefreshWindow();			
		}

		private Project CurrentProject
		{
			get
			{
				if (_currentProject == null)
					_currentProject = new Project();

				return _currentProject;
			}
			set
			{
				_currentProject = value;
			}
		}

		private bool Modified
		{
			get
			{
				return _modified;
			}
			set
			{
				_modified = value;
				SetWindowText(value);
			}
		}

		private void Main_Load(object sender, EventArgs e)
		{
			treeProject.ImageList = CreateProjectTreeImageList();

			string[] args = Environment.GetCommandLineArgs();

			if (args.Length > 1)
			{
				CurrentProject = Project.Load(args[1]);
				Modified = false;
			}

			//CurrentProject = Project.Load("D:\\Sample.dblgen");

			//CurrentProject.ImportPlugin = new PluginInfo(typeof(Plugins.Import.AccessImportPlugin));
			//CurrentProject.ExportPlugin = new PluginInfo(typeof(Plugins.Export.StringTemplateExporter));

			//Plugins.Export.StringTemplateExporterSettings settings = new DBLGen.Plugins.Export.StringTemplateExporterSettings();

			//settings.Templates.Add("D:\\Sample1.stg");
			//settings.Templates.Add("D:\\Sample2.stg");

			//settings.SaveTo(CurrentProject.Settings);

			RefreshWindow();
		}

		private void importTablesToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (CurrentProject.ImportPlugin != null)
			{
				ImportTables frm = new ImportTables(CurrentProject);

				if (frm.ShowDialog() == DialogResult.OK)
				{
					RefreshTree();
					Modified = true;
				}
			}
			else
			{
				DialogResult dr = MessageBox.Show("Currently there is no import plugin selected. Do you want to select one now?", "Import Plugin", MessageBoxButtons.YesNo);

				if (dr == DialogResult.Yes)
				{
					projectSettingsToolStripMenuItem_Click(sender, e);

					if (CurrentProject.ImportPlugin != null)
						importTablesToolStripMenuItem_Click(sender, e);
				}
			}
		}

		private void projectSettingsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ProjectSettings frm = new ProjectSettings(CurrentProject);

			if (frm.ShowDialog() == DialogResult.OK)
			{
				treeProject.SelectedNode = null;
				Modified = true;
			}
		}

		private void pg_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			if (e.ChangedItem.PropertyDescriptor.Name == "SourceType")
				RefreshTree();
			else
				RefreshNodeByTag(pg.SelectedObject);

			Modified = true;
		}

		#region Context Menus

		private void cmsTableFolder_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
		{
			switch (e.ClickedItem.Tag as string)
			{
				case "AddNewTable":

					Table t = new Table();
					t.Name = GetNewTableName();

					switch (cmsTableFolder.Tag as string)
					{
						case "Tables":
							t.SourceType = TableSourceType.Table;
							break;
						case "Views":
							t.SourceType = TableSourceType.View;
							break;
						default:
							t.SourceType = TableSourceType.Custom;
							break;
					}

					CurrentProject.Tables.Add(t);
					RefreshNodeByTag(cmsTableFolder.Tag);
					treeProject.SelectedNode = FindNodeByTag(cmsTableFolder.Tag);

					break;
			}

			Modified = true;
		}

		private void cmsTable_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
		{
			Table t = cmsTable.Tag as Table;

			if (t != null)
			{
				switch (e.ClickedItem.Tag as string)
				{
					case "AddNewColumn":

						Column c = new Column();
						c.Name = GetNewColumnName(t);
						t.Columns.Add(c);

						RefreshNodeByTag(t);
						treeProject.SelectedNode = FindNodeByTag(t);

						break;
					case "Delete":
						{
							if (MessageBox.Show("Are you sure?", "Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
							{
								TreeNode tableNode = FindNodeByTag(t);
								CurrentProject.Tables.Remove(t);

								RefreshNodeByTag(tableNode.Parent.Tag);
							}
							break;
						}
					case "AddCustomMethod":
						{
							Method m = new Method();
							m.Name = GetNewMethodName(t);
							t.Methods.Add(m);
							RefreshNodeByTag(t);
							treeProject.SelectedNode = FindNodeByTag(t);

							break;
						}
				}
			}

			Modified = true;
		}

		private void cmsColumn_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
		{
			if (cmsColumn.Tag is Column)
			{
				#region Column
				Column c = cmsColumn.Tag as Column;
				if (c != null)
				{
					switch (e.ClickedItem.Tag as string)
					{
						case "Delete":

							if (MessageBox.Show("Are you sure?", "Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
							{
								TreeNode columnNode = FindNodeByTag(c);
								Table t = c.Table;
								t.Columns.Remove(c);
								RefreshNodeByTag(t);
								treeProject.SelectedNode = FindNodeByTag(t);
							}

							break;
					}
				}
				#endregion Column
			}
			else if (cmsColumn.Tag is Method)
			{
				#region Method
				Method m = cmsColumn.Tag as Method;

				if (m != null)
				{
					switch (e.ClickedItem.Tag as string)
					{
						case "Delete":
							if (MessageBox.Show("Are you sure?", "Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
							{
								TreeNode columnNode = FindNodeByTag(m);
								Table t = m.Table;
								t.Methods.Remove(m);
								RefreshNodeByTag(t);
								treeProject.SelectedNode = FindNodeByTag(t);
							}
							break;
					}
				}
				#endregion Method
			}

			treeProject.ContextMenuStrip.Hide();

			Modified = true;
		}

		#endregion

		#region Project Tree Management

		private void RefreshTree()
		{
			ProjectNode rootNode = new ProjectNode(CurrentProject.Name, ProjectNodeType.Project, CurrentProject, "folder");
			rootNode.PrepareForRefresh();
			rootNode.Expand();

			treeProject.Nodes.Clear();
			treeProject.Nodes.Add(rootNode);
		}

		public void RefreshNodeByTag(object tag)
		{
			TreeNode node = FindNodeByTag(treeProject.Nodes, tag);

			if (node != null && node.Parent != null)
			{
				ProjectNode parentNode = node.Parent as ProjectNode;
				bool isExpanded = node.IsExpanded;

				if (parentNode != null)
				{
					treeProject.BeginUpdate();

					parentNode.Collapse();
					parentNode.PrepareForRefresh();
					parentNode.Expand();

					if (isExpanded)
					{
						node = FindNodeByTag(parentNode.Nodes, tag);

						if (node != null)
							node.Expand();
					}

					treeProject.EndUpdate();
				}
			}
		}

		private TreeNode FindNodeByTag(object tag)
		{
			return FindNodeByTag(treeProject.Nodes, tag);
		}

		private TreeNode FindNodeByTag(TreeNodeCollection nodes, object tag)
		{
			foreach (TreeNode node in nodes)
			{
				if (node.Tag == tag)
					return node;

				TreeNode subNode = FindNodeByTag(node.Nodes, tag);

				if (subNode != null)
					return subNode;
			}

			return null;
		}

		private void treeProject_MouseClick(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				TreeNode node = treeProject.GetNodeAt(e.Location);

				if (node != null)
					CreateContextMenu(node);
			}
		}

		private void treeProject_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
		{
			treeProject.SelectedNode = e.Node;
		}

		private void treeProject_AfterSelect(object sender, TreeViewEventArgs e)
		{
			object tag = e.Node.Tag;

			if (tag is Table || tag is Column || tag is Method)
			{
				pg.SelectedObject = tag;

				if (CurrentProject.ExportPlugin != null)
					SetExportSettingsEditor();
				else
					ClearExportSettingsEditor();
			}
			else
			{
				pg.SelectedObject = null;
				ClearExportSettingsEditor();
			}
		}

		private void ClearExportSettingsEditor()
		{
			cmbExportSettingsType.SelectedIndex = -1;
			leftSplit.Panel2.Enabled = false;

			if (panelExportSettings.Controls.Count > 0)
				panelExportSettings.Controls.Clear();
		}

		private void SetExportSettingsEditor()
		{
			leftSplit.Panel2.Enabled = true;

			PluginSettingsEditor editor = null;

			if (panelExportSettings.Controls.Count != 1)
			{
				editor = CurrentProject.ExportPlugin.CreateEditor();
				editor.Dock = DockStyle.Fill;

				panelExportSettings.Controls.Clear();
				panelExportSettings.Controls.Add(editor);
			}

			Table t = GetSelectedTable();
			ExportSettingsType settingsType = GetSelectedTableExportSettingsType(t);

			int oldSelectedIndex = cmbExportSettingsType.SelectedIndex;

			switch (settingsType)
			{
				case ExportSettingsType.Global:
					cmbExportSettingsType.SelectedIndex = 0;

					if (oldSelectedIndex == 0)
						cmbExportSettingsType_SelectedIndexChanged(this, EventArgs.Empty);

					break;
				case ExportSettingsType.Table:
					cmbExportSettingsType.SelectedIndex = 1;

					if (oldSelectedIndex == 1)
						cmbExportSettingsType_SelectedIndexChanged(this, EventArgs.Empty);

					break;
			}
		}

		private ExportSettingsType GetSelectedTableExportSettingsType(Table t)
		{
			if (CurrentProject.SettingsManager.Contains(CurrentProject.ExportPlugin.SettingsType, t.UniqueID.ToString()))
				return ExportSettingsType.Table;
			else
				return ExportSettingsType.Global;
		}

		private void cmbExportSettingsType_SelectedIndexChanged(object sender, EventArgs e)
		{
			int selectedIndex = cmbExportSettingsType.SelectedIndex;

			if (selectedIndex != -1)
			{
				Table t = GetSelectedTable();
				PluginSettings settings = CurrentProject.ExportPlugin.CreateSettings();
				PluginSettingsEditor editor = (PluginSettingsEditor)panelExportSettings.Controls[0];

				if (selectedIndex == 0)
				{
					settings.LoadFrom(CurrentProject.SettingsManager);
					panelExportSettings.Enabled = false;
				}
				else if (selectedIndex == 1)
				{
					if (!settings.LoadFrom(CurrentProject.SettingsManager, t.UniqueID.ToString()))
						settings.LoadFrom(CurrentProject.SettingsManager);

					panelExportSettings.Enabled = true;
				}

				editor.LoadSettings(settings);
			}
		}

		private void treeProject_BeforeExpand(object sender, TreeViewCancelEventArgs e)
		{
			ProjectNode node = e.Node as ProjectNode;

			if (node != null && node.NeedsRefresh)
			{
				Project p = node.Tag as Project;

				if (p != null)
				{
					BuildTableFolderNodes(node);
					return;
				}

				string folder = node.Tag as string;
				node.Nodes.Clear();

				if (folder != null)
				{
					Table[] tables = null;

					switch (folder)
					{
						case "Tables":
							tables = CurrentProject.Tables.SelectBySourceType(TableSourceType.Table);
							break;
						case "Views":
							tables = CurrentProject.Tables.SelectBySourceType(TableSourceType.View);
							break;
						case "Custom":
							tables = CurrentProject.Tables.SelectBySourceType(TableSourceType.Custom);
							break;
					}

					Array.Sort<Table>(tables, TableComparer);
					BuildTableNodes(node, tables);

					return;
				}

				Table table = node.Tag as Table;

				if (table != null)
				{
					BuildColumnNodes(node, table.Columns);
					BuildMethodNodes(node, table.Methods);
					return;
				}
			}
		}

		private static int TableComparer(Table a, Table b)
		{
			return String.Compare(a.QualifiedName, b.QualifiedName, true);
		}

		private void BuildTableFolderNodes(TreeNode parentNode)
		{
			parentNode.Nodes.Clear();

			ProjectNode tablesNode = new ProjectNode("Tables", ProjectNodeType.TableFolder, "Tables", "folder");
			tablesNode.PrepareForRefresh();

			ProjectNode viewsNode = new ProjectNode("Views", ProjectNodeType.TableFolder, "Views", "folder");
			viewsNode.PrepareForRefresh();

			ProjectNode customNode = new ProjectNode("Custom", ProjectNodeType.TableFolder, "Custom", "folder");
			customNode.PrepareForRefresh();

			parentNode.Nodes.Add(tablesNode);
			parentNode.Nodes.Add(viewsNode);
			parentNode.Nodes.Add(customNode);

			tablesNode.Expand();
			viewsNode.Expand();
			customNode.Expand();
		}

		private void BuildTableNodes(TreeNode parentNode, Table[] tables)
		{
			foreach (Table t in tables)
			{
				ProjectNode node = new ProjectNode(t.QualifiedName, ProjectNodeType.Table, t, "table");
				node.PrepareForRefresh();
				parentNode.Nodes.Add(node);
			}
		}

		private void BuildMethodNodes(TreeNode parentNode, MethodCollection methods)
		{
			foreach (Method m in methods)
			{
				string imageKey = "gscript";

				//if (c.IsPrimaryKey)
				//  imageKey = "column_pk";
				//else if (c.IsForeignKey)
				//  imageKey = "column_fk";
				//else
				//  imageKey = "column";

				ProjectNode node = new ProjectNode(m.Name, ProjectNodeType.Method, m, imageKey);

				//if (c.IsForeignKey)
				//    node.AddDummyNode();

				parentNode.Nodes.Add(node);
			}
		}

		private void BuildColumnNodes(TreeNode parentNode, ColumnCollection columns)
		{
			foreach (Column c in columns)
			{
				string imageKey;

				if (c.IsPrimaryKey)
					imageKey = "column_pk";
				else if (c.IsForeignKey)
					imageKey = "column_fk";
				else
					imageKey = "column";

				ProjectNode node = new ProjectNode(c.Name, ProjectNodeType.Column, c, imageKey);

				//if (c.IsForeignKey)
				//    node.AddDummyNode();

				parentNode.Nodes.Add(node);
			}
		}

		#endregion

		#region Helper Methods

		private string GetNewTableName()
		{
			int i = 0;
			string name;

			do
			{
				i++;
				name = "_NewTable" + i;
			}
			while (CurrentProject.Tables.Find(String.Empty, name) != null);

			return name;
		}

		private string GetNewColumnName(Table target)
		{
			int i = 0;
			string name;

			do
			{
				i++;
				name = "NewColumn" + i;
			}
			while (target.Columns.Find(name) != null);

			return name;
		}

		private string GetNewMethodName(Table target)
		{
			int i = 0;
			string name;

			do
			{
				i++;
				name = "Method" + i;
			}
			while (target.Columns.Find(name) != null);

			return name;
		}

		private ImageList CreateProjectTreeImageList()
		{
			ImageList il = new ImageList();

			il.ColorDepth = ColorDepth.Depth32Bit;
			il.ImageSize = new Size(16, 16);

			il.Images.Add("folder", Icons.folder);
			il.Images.Add("table", Icons.table);
			il.Images.Add("column", Icons.column);
			il.Images.Add("column_pk", Icons.column_pk);
			il.Images.Add("column_fk", Icons.column_fk);
			il.Images.Add("script", Icons.script);
			il.Images.Add("gscript", Icons.gscript);

			return il;
		}

		private void CreateContextMenu(TreeNode node)
		{
			Table table = node.Tag as Table;

			if (table != null)
			{
				cmsTable.Tag = table;
				treeProject.ContextMenuStrip = cmsTable;
				return;
			}

			Column column = node.Tag as Column;

			if (column != null)
			{
				cmsColumn.Tag = column;
				treeProject.ContextMenuStrip = cmsColumn;
				return;
			}

			Method method = node.Tag as Method;

			if (method != null)
			{
				cmsColumn.Tag = method;
				treeProject.ContextMenuStrip = cmsColumn;
				return;
			}

			string text = node.Tag as string;

			if (text != null)
			{
				cmsTableFolder.Tag = text;
				treeProject.ContextMenuStrip = cmsTableFolder;
				return;
			}

			treeProject.ContextMenuStrip = null;
		}

		private Table GetCurrentSelectedTable()
		{
			TreeNode selectedNode = treeProject.SelectedNode;

			if (selectedNode != null)
			{
				Table t = selectedNode.Tag as Table;

				if (t != null)
					return t;

				Column c = selectedNode.Tag as Column;

				if (c != null)
					return c.Table;


				Method m = selectedNode.Tag as Method;
				if (m != null)
					return m.Table;
			}

			return null;
		}

		private PluginSettings GetExportSettings(Table table)
		{
			PluginInfo pi = CurrentProject.ExportPlugin;

			PluginSettings settings = null;

			if (pi != null)
			{
				settings = pi.CreateSettings();

				if (settings != null)
				{
					if (settings.LoadFrom(CurrentProject.SettingsManager, table.UniqueID.ToString()))
						return settings;

					if (settings.LoadFrom(CurrentProject.SettingsManager))
						return settings;
				}
			}

			return settings;
		}

		private void RefreshWindow()
		{
			pg.SelectedObject = null;
			ClearExportSettingsEditor();
			SetWindowText(Modified);
			lblProjectName.Text = CurrentProject.Name;
			RefreshTree();
		}

		private void SetWindowText(bool modified)
		{
			if (modified)
				this.Text = CurrentProject.Name + "*";
			else
				this.Text = CurrentProject.Name + "";
		}

		private Table GetSelectedTable()
		{
			object selectedObject = pg.SelectedObject;

			if (selectedObject != null)
			{
				Table t = selectedObject as Table;

				if (selectedObject is Column)
				{
					if (t == null)
						t = ((Column)selectedObject).Table;
				}
				else if (selectedObject is Method)
				{
					if (t == null)
						t = ((Method)selectedObject).Table;
				}

				return t;
			}

			return null;
		}

		private bool ConfirmLosingChanges()
		{
			return MessageBox.Show("You have unsaved changes. Do you want to continue?", "Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes;
		}

		#endregion

		private void linkExportPreview_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			leftSplit_Panel2_Leave(sender, e);

			PluginInfo pi = CurrentProject.ExportPlugin;

			if (pi != null)
			{
				Table t = GetCurrentSelectedTable();

				if (t != null)
				{
					ExportPlugin ep = pi.CreatePlugin() as ExportPlugin;
					ep.Settings = GetExportSettings(t);

					try
					{
						ExportDataCollection edc = ep.Export(t);

						using (ExportPreview frm = new ExportPreview(edc))
						{
							frm.ShowDialog();
						}
					}
					catch (ErrorMessageListException ex)
					{
						using (ShowErrorList frm = new ShowErrorList(ex.Messages))
						{
							frm.ShowDialog();
						}
					}
				}
			}
		}

		private void newToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (Modified && !ConfirmLosingChanges())
				return;

			CurrentProject = null;
			Modified = false;
			RefreshWindow();
		}

		private void openToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (Modified && !ConfirmLosingChanges())
				return;

			using (OpenFileDialog ofd = new OpenFileDialog())
			{
				ofd.Filter = FileDialogFilter;
				ofd.FilterIndex = 0;
				ofd.InitialDirectory = Environment.CurrentDirectory;

				if (ofd.ShowDialog() == DialogResult.OK)
				{
					CurrentProject = Project.Load(ofd.FileName);
					Modified = false;
					RefreshWindow();
				}
			}
		}


		private void saveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (File.Exists(CurrentProject.FilePath))
			{
				CurrentProject.Save();
				Modified = false;
				RefreshWindow();
			}
			else
				saveAsToolStripMenuItem_Click(sender, e);
		}

		private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (SaveFileDialog sfd = new SaveFileDialog())
			{
				sfd.Filter = FileDialogFilter;
				sfd.FilterIndex = 0;
				sfd.FileName = CurrentProject.Name + ".dblgen";

				if (sfd.ShowDialog() == DialogResult.OK)
				{
					CurrentProject.Save(sfd.FileName);
					Modified = false;
					RefreshWindow();
				}
			}
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void Main_FormClosing(object sender, FormClosingEventArgs e)
		{
			treeProject.Focus();

			if (Modified)
			{
				DialogResult result = MessageBox.Show("Do you want to save your project before closing?", "Confirm", MessageBoxButtons.YesNoCancel);

				switch (result)
				{
					case DialogResult.Yes:
						saveToolStripMenuItem_Click(sender, EventArgs.Empty);

						if (Modified)
							e.Cancel = true;

						break;
					case DialogResult.Cancel:
						e.Cancel = true;
						break;
				}
			}
		}

		private void leftSplit_Panel2_Leave(object sender, EventArgs e)
		{
			Table t = GetSelectedTable();

			if (cmbExportSettingsType.SelectedIndex == 0)
			{
				CurrentProject.SettingsManager.Delete(CurrentProject.ExportPlugin.SettingsType, t.UniqueID.ToString());
			}
			else if (cmbExportSettingsType.SelectedIndex == 1)
			{
				PluginSettingsEditor editor = (PluginSettingsEditor)panelExportSettings.Controls[0];
				PluginSettings settings = editor.SaveSettings();

				settings.SaveTo(CurrentProject.SettingsManager, t.UniqueID.ToString());
			}

			Modified = true;
		}

		private void mainMenuStrip_MenuActivate(object sender, EventArgs e)
		{
			mainMenuStrip.Focus();
		}

		private void runBatchToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Table[] allTables = CurrentProject.Tables.ToArray();
			Array.Sort(allTables, TableComparer);

			using (RunBatch frm = new RunBatch(allTables))
			{
				frm.ShowDialog();
			}
		}

		private void exportToFilesToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (Export frm = new Export(CurrentProject))
			{
				frm.ShowDialog();
			}
		}

		private void newToolStripButton_Click(object sender, EventArgs e)
		{
			newToolStripMenuItem_Click(sender, e);
		}

		private void saveToolStripButton_Click(object sender, EventArgs e)
		{
			saveToolStripMenuItem_Click(sender, e);
		}

		private void importTablesToolStripButton_Click(object sender, EventArgs e)
		{
			importTablesToolStripMenuItem_Click(sender, e);
		}

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (About frm = new About())
			{
				frm.ShowDialog();
			}
		}

		private void linkProjectSettings_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			projectSettingsToolStripMenuItem_Click(sender, e);
		}
	}
}
